package com.zhaokuner.parentstarter.utils;

import com.alibaba.fastjson.JSON;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.web.method.HandlerMethod;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Description:bean 属性拷贝，浅拷贝，Date 和 List也会转换
 * <p>
 * 通过相同fieldName判定拷贝对象
 *
 * @author 赵希坤 zhaoxikun
 * @date 2020/12/15 13:15
 */
@Log4j2
public class MyCopyUtil {

    public interface FieldConverter {
        /**
         * 不同类型的数据转换
         *
         * @param value             源数据
         * @param targetValueMethod 目标数据的方法
         * @param targetObject      目标对象
         * @return 转换后的数据
         */
        Object convert(Object value, BeanReflectUtil.RwMethod targetValueMethod, Object targetObject);
    }

    private static ConcurrentHashMap<Method, Type> generic = new ConcurrentHashMap<>();

    public static Type getGeneric(Class<?> clazz, Method method) {
        Type result = generic.get(method);
        if (null != result) {
            if (void.class.equals(result)) {
                return null;
            }
            return result;
        }
        HandlerMethod handlerMethod = new HandlerMethod(clazz, method);
        MethodParameter[] ps = handlerMethod.getMethodParameters();
        if (ps.length > 0) {
            SynthesizingMethodParameter ps0 = (SynthesizingMethodParameter) ps[0];
            Type type = ps0.getGenericParameterType();
            if (null != type && type instanceof ParameterizedTypeImpl) {
                Type[] types = ((ParameterizedTypeImpl) type).getActualTypeArguments();
                if (null != types && types.length > 0) {
                    Type returnType = types[0];
                    if (returnType instanceof WildcardTypeImpl) {
                        generic.put(method, void.class);
                        returnType = null;
                    } else {
                        generic.put(method, returnType);
                    }
                    return returnType;
                }
            }
        }
        generic.put(method, void.class);
        return null;
    }

    public static FieldConverter defaultConverter = (value, targetMethod, targetObject) -> {
        //相同类型
        if (value.equals(targetMethod.field.getType())) {
            return value;
        }
        //类型不相同

        // 1 <-> true 0 <-> false
        if (Integer.class.equals(value.getClass()) && Boolean.class.equals(targetMethod.field.getType())) {
            return 0 != (Integer) value;
        } else if (Boolean.class.equals(value.getClass()) && Integer.class.equals(targetMethod.field.getType())) {
            return (boolean) value ? 1 : 0;
        }
        //是否为枚举 String -> enum
        if (targetMethod.field.getType().isEnum()) {
            return Enum.valueOf((Class<Enum>) targetMethod.field.getType(), value.toString());
        }
        Class<?> targetClass = targetMethod.field.getType();
        //处理日期 为数字long或数字string
        if (value instanceof Date) {
            if (Long.class.equals(targetClass)) {
                return ((Date) value).getTime();
            }
            //String类型的时间戳
            if (String.class.equals(targetClass)) {
                return String.valueOf(((Date) value).getTime());
            }
        }
        //是否是string
        boolean valueStringClass = value instanceof CharSequence;
        //处理字符格式日期 String -> date
        if (valueStringClass && Date.class.isAssignableFrom(targetClass)) {
            //时间戳字符串
            if (StringUtils.isNumeric(value.toString())) {
                return JacksonUtils.readValue(value.toString(), targetClass);
            }
            //yyyy-mm-dd hh:mm:ss[.fffffffff]
            try {
                Timestamp timestamp = Timestamp.valueOf(value.toString());
                switch (targetClass.getName()) {
                    case "java.sql.Timestamp":
                    case "java.util.Date":
                        return timestamp;
                    case "java.sql.Time":
                        return new java.sql.Time(timestamp.getTime());
                    case "java.sql.Date":
                        return new java.sql.Date(timestamp.getTime());
                    default:
                        return JacksonUtils.readValue(String.valueOf(timestamp.getTime()), targetClass);
                }
            } catch (Exception e) {
                return null;
            }
        }
        //处理其他 json 也可以将long转换为date
        String str;
        if (valueStringClass) {
            str = value.toString();
        } else {
            str = JacksonUtils.toJson(value);
        }

        if (targetClass.isAssignableFrom(String.class)) {
            return str;
        }
        //处理列表，get方法的返回值，就是set方法的请求值
        Type returnType = targetMethod.readMethod.getJavaMethod().getGenericReturnType();
        return JSON.parseObject(str, returnType);
    };


    /**
     * 对象copy工具函数
     *
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copy(Object source, Object target) {
        copy(source, target, defaultConverter);
    }

    /**
     * copy map to bean
     *
     * @param source Map源对象 map中的key 转为驼峰
     * @param target Bean目标对象
     */
    public static void copyMapToCamel(Map<String, ?> source, Object target) {
        copyMapToCamel(source, target, defaultConverter);
    }

    /**
     * copy map to bean
     *
     * @param source    Map源对象 map中的key 转为驼峰
     * @param target    Bean目标对象
     * @param converter 数据类型不相同时数据转换
     */
    public static void copyMapToCamel(Map<String, ?> source, Object target, FieldConverter converter) {
        //设置underlineToCamel
        LinkedHashMap<String, Object> tempMap = new LinkedHashMap<>(8);
        source.forEach((key, value) -> {
            tempMap.put(BeanReflectUtil.underlineToCamel(key), value);
        });

        copyMap(tempMap, target, converter);
    }

    /**
     * copy map to bean
     *
     * @param source    Map源对象
     * @param target    Bean目标对象
     * @param converter 数据类型不相同时数据转换
     */
    public static void copyMap(Map<String, ?> source, Object target, FieldConverter converter) {
        if (null == source || source.isEmpty() || null == target) {
            return;
        }
        if (null == converter) {
            converter = defaultConverter;
        }

        final FieldConverter finalConverter = converter;
        LinkedHashMap<String, BeanReflectUtil.RwMethod> tMap = BeanReflectUtil.getRWMethod(target);
        source.forEach((k, v) -> {
            BeanReflectUtil.RwMethod rwMethod = tMap.get(k);
            if (null == k || null == rwMethod) {
                return;
            }
            Object value = finalConverter.convert(v, rwMethod, target);
            if (null != value) {
                rwMethod.set(target, value);
            }
        });
    }

    /**
     * 对象copy工具函数
     *
     * @param source    源对象
     * @param target    目标对象
     * @param converter 数据类型不相同时数据转换
     */
    public static void copy(Object source, Object target, FieldConverter converter) {
        if (null == source || null == target) {
            return;
        }
        if (source instanceof Map) {
            copyMap((Map<String, ?>) source, target, converter);
        }
        LinkedHashMap<String, BeanReflectUtil.RwMethod> sMap = BeanReflectUtil.getRWMethod(source);
        LinkedHashMap<String, BeanReflectUtil.RwMethod> tMap = BeanReflectUtil.getRWMethod(target);
        tMap.forEach((name, rwMethod) -> {
            BeanReflectUtil.RwMethod rwm = sMap.get(name);
            if (null == rwm) {
                return;
            }

            if (null != converter) {
                Object sv = rwm.get(source);
                if (null == sv) {
                    return;
                }
                Object value = converter.convert(sv, rwMethod, target);
                if (null != value) {
                    rwMethod.set(target, value);
                }
            } else {
                //相同类型
                if (rwMethod.field.getType().equals(rwm.field.getType())) {
                    Object sv = rwm.get(source);
                    if (null == sv) {
                        return;
                    }
                    rwMethod.set(target, sv);
                }
                //不同类型 不处理
            }
        });

    }

}
